#include "lfs.h"
#include "rtthread.h"
#include "slog.h"
#include "bsp_w25qxx.h"

#define SPI_FLASH_FS_BLOCK_OFF       2560     /* 10M֮��Ŀռ�����littfs */
#define SPI_FLASH_FS_BASE_ADDR_OFF      0xA00000

static int lfs_spi_flash_init(struct lfs_config *cfg);
static int lfs_spi_flash_read(const struct lfs_config *cfg, lfs_block_t block, lfs_off_t off, void *buffer, lfs_size_t size);
static int lfs_spi_flash_prog(const struct lfs_config *cfg, lfs_block_t block, lfs_off_t off, const void *buffer, lfs_size_t size);
static int lfs_spi_flash_erase(const struct lfs_config *cfg, lfs_block_t block);
static int lfs_spi_flash_sync(const struct lfs_config *cfg);
static int lfs_spi_lock(const struct lfs_config *c);
static int lfs_spi_unlock(const struct lfs_config *c);
///
/// ��̬�ڴ�ʹ�÷�ʽ�����趨���ĸ�����
///
#ifdef LFS_NO_MALLOC
__align(4) static uint8_t read_buffer[16];
__align(4) static uint8_t prog_buffer[16];
__align(4) static uint8_t lookahead_buffer[16];
#endif

rt_mutex_t spiflash_mutex_lock;

static int lfs_spi_flash_init(struct lfs_config *cfg) 
{  
    uint32_t id = W25QXX_ReadJedDevID();

    if (XM25QH128A_ID == id) 
	{
        SLOG_PRINT("FlashID %x %x %d\r\n", id, W25QXX_ReadManufactDevID(), W25QXX_ReadDeviceID());

        spiflash_mutex_lock = rt_mutex_create("lfslock", RT_IPC_FLAG_FIFO);
        if (spiflash_mutex_lock == RT_NULL) SLOG_PRINT("lfslock err!\r\n");

        cfg->read = lfs_spi_flash_read;
        cfg->prog = lfs_spi_flash_prog;
        cfg->erase = lfs_spi_flash_erase;
        cfg->sync = lfs_spi_flash_sync;
#ifdef LFS_THREADSAFE
        cfg->lock = lfs_spi_lock;
        cfg->unlock = lfs_spi_unlock;
#endif
        // ��С��ȡ�ֽ��������еĶ�ȡ�����ֽ�������������������
        cfg->read_size = 16;
        // ��Сд���ֽ��������е�д������ֽ�������������������
        cfg->prog_size = 16;
        // ������������ֽ�������ѡ�Ӱ�� RAM ���ģ����Ա����������ߴ��
        // ����ÿ���ļ�����ռ��һ���飬�����Ƕ�ȡ��д������ֽ�����������
        cfg->block_size = 4096;
        // �豸�Ͽɲ������������������
        cfg->block_count = 1536;
        // littlefs ϵͳɾ��Ԫ������־����Ԫ�����ƶ�����һ����֮ǰ�Ĳ�����������
        // ����ȡֵ��ΧΪ 100 ~ 1000���ϴ���ֵ�нϺõ����ܵ��ǻᵼ��ĥ��ֲ���һ��
        // ȡֵ -1 �Ļ�����Ϊ���ÿ鼶ĥ�����
        cfg->block_cycles = 500;
        // �黺���С��ÿ�����涼���� RAM �л���һ���ֿ����ݣ�
        // littlefs ϵͳ��Ҫһ����ȡ���桢һ��д�뻺�棬ÿ���ļ�����Ҫһ������Ļ��档
        // ����Ļ������ͨ���洢��������ݲ����ʹ��̷����������ֶ����������
        cfg->cache_size = 16;
        // ���л����С����������л��������߷�������пɱ����ֵĿ�����
        // �������ʱÿ�β������ٸ��飬16�ͱ�ʾÿ�η���16����
        // ���л����Խ��յ�bitλ��ʽ���洢���� RAM �е�һ���ֽڿ��Զ�Ӧ8����
        // ��ֵ������8��������
        cfg->lookahead_size = 16;
#ifdef LFS_NO_MALLOC
        cfg->read_buffer = read_buffer;
        cfg->prog_buffer = prog_buffer;
        cfg->lookahead_buffer = lookahead_buffer;
#endif
        return LFS_ERR_OK;
    } else {
        return LFS_ERR_IO;
    }
}

/*
 * @brief ��ָ�����ڵ�ĳ���������
 * @param [in] lfs_config��ʽ����
 * @param [in] block �߼��������ţ���0��ʼ
 * @param [in] off ����ƫ�ƣ���ֵ���ܱ�read_size����
 * @param [out] �������ݵ����������
 * @param [in] size Ҫ��ȡ���ֽ�������ֵ���ܱ�read_size������lfs�ڶ�ȡʱ��ȷ�������飻
 * @retval 0 �ɹ�, < 0 ������
 */
static int lfs_spi_flash_read(const struct lfs_config *cfg, lfs_block_t block, lfs_off_t off, void *buffer, lfs_size_t size) {
    
    // check if read is valid
    LFS_ASSERT(off % cfg->read_size == 0);
    LFS_ASSERT(size % cfg->read_size == 0);
    LFS_ASSERT(block < (cfg->block_count));
    LFS_ASSERT(block < cfg->block_count);
    
    block += SPI_FLASH_FS_BLOCK_OFF;
	W25QXX_Read((uint8_t *)buffer, cfg->block_size * block + off , size);
    return LFS_ERR_OK;
}

/*
 * @brief ������д��ָ�����ڵ�ĳ���򡣸���������Ѿ��ȱ������������Է��� LFS_ERR_CORRUPT ��ʾ�ÿ�����
 * @param [in] lfs_config��ʽ����
 * @param [in] block �߼��������ţ���0��ʼ
 * @param [in] off ����ƫ�ƣ���ֵ���ܱ�rprog_size����
 * @param [in] д�����ݵĻ�����
 * @param [in] size Ҫд����ֽ�������ֵ���ܱ�read_size������lfs�ڶ�ȡʱ��ȷ�������飻
 * @retval 0 �ɹ�, < 0 ������
 */
static int lfs_spi_flash_prog(const struct lfs_config *cfg, lfs_block_t block, lfs_off_t off, const void *buffer, lfs_size_t size) {
    // check if write is valid
    LFS_ASSERT(off % cfg->prog_size == 0);
    LFS_ASSERT(size % cfg->prog_size == 0);
    LFS_ASSERT(block < (cfg->block_count));
    // LFS_ASSERT(block < cfg->block_count);
    block += SPI_FLASH_FS_BLOCK_OFF;
    W25QXX_Write_NoCheck((uint8_t *)buffer,  cfg->block_size * block + off, size);
    return LFS_ERR_OK;
}

/*
 * @brief ����ָ���顣����д��֮ǰ�����ȱ������������������״̬��δ����
 * @param [in] lfs_config��ʽ����
 * @param [in] block Ҫ�������߼��������ţ���0��ʼ
 * @retval 0 �ɹ�, < 0 ������
 */
static int lfs_spi_flash_erase(const struct lfs_config *cfg, lfs_block_t block) {
   // check if erase is valid
      LFS_ASSERT(block < cfg->block_count);
//    LFS_ASSERT(block < cfg->block_count);
	lfs_block_t _block = block + SPI_FLASH_FS_BLOCK_OFF;

    W25QXX_Erase_Sector(_block);
    // SPI_FLASH_EraseSector(blockAddr * cfg->block_size);
    return LFS_ERR_OK;
}

/*
 * @brief �Եײ���豸��ͬ�����������ײ���豸��û��ͬ�������������ֱ�ӷ���
 * @param [in] lfs_config��ʽ����;
 * @retval 0 �ɹ�, < 0 ������
 */
static int lfs_spi_flash_sync(const struct lfs_config *cfg) {
    return LFS_ERR_OK;
}

static int lfs_spi_lock(const struct lfs_config *c)
{
   if (spiflash_mutex_lock != RT_NULL)
       rt_mutex_take(spiflash_mutex_lock, RT_WAITING_FOREVER);

    //  rt_enter_critical();
    return 0;
}

    // Unlock the underlying block device. Negative error codes
    // are propogated to the user.
static int lfs_spi_unlock(const struct lfs_config *c)
{
   if (spiflash_mutex_lock != RT_NULL)
       rt_mutex_release(spiflash_mutex_lock);

    // rt_exit_critical();	
   return 0;
}

lfs_t lfs;    /* 文件系统对象 */
lfs_file_t file;  /* 文件句柄 */
static struct lfs_config cfg;   /* 文件配置 */

void LittleFs_Init(void) 
{
    lfs_spi_flash_init(&cfg);  
    // mount the filesystem
    int err = lfs_mount(&lfs, &cfg);

    // reformat if we can't mount the filesystem
    // this should only happen on the first boot
    SLOG_PRINT("mount err %d\r\n", err);
    // err = lfs_format(&lfs, &cfg);
    if (err) 
    {
        err = lfs_format(&lfs, &cfg);
        SLOG_PRINT("fmt err %d\r\n", err);
        err = lfs_mount(&lfs, &cfg);
        SLOG_PRINT("mount err %d\r\n", err);
    }
    
    // read current count
    uint32_t boot_count = 0;
    err = lfs_file_open(&lfs, &file, "boot_count", LFS_O_RDWR | LFS_O_CREAT);
    err = lfs_file_read(&lfs, &file, &boot_count, sizeof(boot_count));

    // update boot count
    boot_count += 1;
    lfs_file_rewind(&lfs, &file);
    lfs_file_write(&lfs, &file, &boot_count, sizeof(boot_count));

    // remember the storage is not updated until the file is closed successfully
    lfs_file_close(&lfs, &file);
    
    // print the boot count
    SLOG_PRINT("boot_count: %d\r\n", boot_count);
}


